package controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

import classes.Content;
import classes.Contribution;
import classes.Credits;
import classes.LearningObject;
import classes.Link;
import classes.MainLink;
import classes.Menu;
import classes.Page;
import classes.SimpleImage;
import classes.SimpleText;

public class EditorInterfaceController
{
   private Document file;
   XStream xstream = new XStream( new DomDriver() );

   protected LearningObject object = new LearningObject();
   protected String fileName = "";
   
   /**
    * Carrega um arquivo xml.
    * 
	 * @param fileName Caminho do arquivo que será aberto.
	 */
	public void openFile( String filePath )
   {
	  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware( false );
      DocumentBuilder docBuilder;
      try
      {
         docBuilder = dbf.newDocumentBuilder();
         file = docBuilder.parse( new File( filePath ) );
      }
      catch ( ParserConfigurationException e )
      {
         e.printStackTrace();
      }
      catch ( SAXException e )
      {
         e.printStackTrace();
      }
      catch ( IOException e )
      {
         e.printStackTrace();
      }

      loadObject();
      System.out.println( "Objeto carregado com sucesso." );
   }

	/**
	* Salva o objeto corrente no formato xml.
	*
	* @return 1 caso o salvamento seja efetuado com sucesso ou 0 caso contrário.
	*/
	public int saveFile()
	{
		xstream.registerConverter( new LearningObjectConverter() );
		
		xstream.alias("object", LearningObject.class);
		String objectXML = xstream.toXML( object );
		   
		try {
			OutputStream OS = (OutputStream) new FileOutputStream(new File( fileName ));  
			OutputStreamWriter OSW = new OutputStreamWriter(OS, "UTF8");  
			PrintWriter Print = new PrintWriter(OSW);  
			Print.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");  
			Print.println(objectXML);
			Print.close();  
			OSW.close();  
			OS.close();
		} catch (IOException e) {
			e.printStackTrace();
			return 0;
		}  
		
		System.out.println( "Arquivo salvo com sucesso." );
		return 1;
	}
   
   public void setFile( Document file )
   {
      this.file = file;
   }

   public Document getFile()
   {
      return file;
   }

   /**
    * Função que traduz o xml em um objeto.
    */
   private void loadObject()
   {
      setGeneralProperties();
      setPagesProperties();
   }

   /**
    * Busca e armazena as propriedades gerais do objeto.
    */
   private void setGeneralProperties()
   {
      Element root = file.getDocumentElement();
      
      // Seta o nome do objeto.
      Element general = (Element) root.getElementsByTagName( "General" ).item( 0 );

      Element name = (Element) general.getElementsByTagName( "name" ).item( 0 );
      object.setName( name.getTextContent() );
      
      // Seta o header do objeto.
      Element headerXML = (Element) general.getElementsByTagName( "Header" ).item( 0 );
      Element extension = (Element) headerXML.getElementsByTagName( "extension" ).item( 0 );
      Element path = (Element) headerXML.getElementsByTagName( "path" ).item( 0 );
      Element width = (Element) headerXML.getElementsByTagName( "width" ).item( 0 );
      Element height = (Element) headerXML.getElementsByTagName( "height" )
            .item( 0 );

      SimpleImage header = new SimpleImage();

      header.setProperties( extension.getTextContent(), path.getTextContent(),
            width.getTextContent(), height.getTextContent() );
      object.setHeader( header );
      
      // Seta o footer do objeto.
      Element footerXML = (Element) general.getElementsByTagName( "Footer" )
            .item( 0 );
      extension = (Element) footerXML.getElementsByTagName( "extension" ).item(
            0 );
      path = (Element) footerXML.getElementsByTagName( "path" ).item( 0 );
      width = (Element) footerXML.getElementsByTagName( "width" ).item( 0 );
      height = (Element) footerXML.getElementsByTagName( "height" ).item( 0 );

      SimpleImage footer = new SimpleImage();

      footer.setProperties( extension.getTextContent(), path.getTextContent(),
            width.getTextContent(), height.getTextContent() );
      object.setFooter( footer );

      setCreditsProperties( general );
      setMenuProperties( general );
   }

   /**
    * Busca e armazena as propriedades dos créditos.
    */
   private void setCreditsProperties( Element general )
   {
      // Gera o objeto que irá conter as informações dos créditos.
      Element creditsXML = (Element) general.getElementsByTagName( "Credits" )
            .item( 0 );

      Element titleXML = (Element) creditsXML.getElementsByTagName( "Title" )
            .item( 0 );
      Element text = (Element) titleXML.getElementsByTagName( "text" ).item( 0 );
      Element font = (Element) titleXML.getElementsByTagName( "font" ).item( 0 );
      Element size = (Element) titleXML.getElementsByTagName( "size" ).item( 0 );

      SimpleText title = new SimpleText();
      title.setProperties( text.getTextContent(), font.getTextContent(),
            size.getTextContent() );

      NodeList contributionsXML = creditsXML
            .getElementsByTagName( "Contribution" );
      ArrayList<Contribution> contributions = new ArrayList<Contribution>();
      for( int i=0; i<contributionsXML.getLength(); i++ )
      {
         Contribution c = new Contribution();
         Element contr = (Element) contributionsXML.item( i );
         Element name = (Element) contr.getElementsByTagName( "name" ).item( 0 );
         Element role = (Element) contr.getElementsByTagName( "role" ).item( 0 );
         Element titleC = (Element) contr.getElementsByTagName( "title" ).item(
               0 );
         Element entity = (Element) contr.getElementsByTagName( "entity" )
               .item( 0 );

         c.setProperties( i, name.getTextContent(), role.getTextContent(),
               titleC.getTextContent(), entity.getTextContent() );
         contributions.add( c );
      }

      Credits credits = new Credits();
      credits.setProperties( title, contributions );
      object.setCredits( credits );
   }

   /**
    * Busca e armazena as propriedades do menu.
    */
   private void setMenuProperties( Element general )
   {
      // Gera o array com os links.
      Element menuXML = (Element) general.getElementsByTagName( "Menu" ).item(
            0 );
      
      NodeList titleXML = menuXML.getElementsByTagName( "Title" );
      ArrayList<MainLink> links = new ArrayList<MainLink>();
      for ( int i = 0; i < titleXML.getLength(); i++ )
      {
         MainLink l = new MainLink();
         Element link = (Element) titleXML.item( i );
         Element text = (Element) link.getElementsByTagName( "text" ).item( 0 );
         Element idlinkpage = (Element) link
               .getElementsByTagName( "idlinkpage" ).item( 0 );
         l.setProperties( i, text.getTextContent(), idlinkpage.getTextContent() );

         // Gera o array com os sub-links.
         NodeList subLinksXML = link.getElementsByTagName( "SubTitle" );
         ArrayList<Link> subLinks = new ArrayList<Link>();
         for ( int j = 0; j < subLinksXML.getLength(); j++ )
         {
            Link sl = new Link();
            Element subLink = (Element) subLinksXML.item( j );
            text = (Element) subLink.getElementsByTagName( "text" ).item( 0 );
            idlinkpage = (Element) subLink.getElementsByTagName( "idlinkpage" )
                  .item( 0 );
            sl.setProperties( j, text.getTextContent(),
                  idlinkpage.getTextContent() );

            subLinks.add( sl );
         }

         l.setSubLinks( subLinks );
         links.add( l );
      }
      
      Menu menu = new Menu();
      menu.setLinks( links );
      object.setMenu( menu );
   }

   /**
    * Busca e armazena as propriedades das páginas.
    */
   private void setPagesProperties()
   {
      Element root = file.getDocumentElement();

      // Gera o array com as páginas.
      NodeList pagesOfDoc = root.getElementsByTagName( "Page" );
      ArrayList<Page> pages = new ArrayList<Page>();
      for ( int i = 0; i < pagesOfDoc.getLength(); i++ )
      {
         Page p = new Page();
         Element pageXML = (Element) pagesOfDoc.item( i ); // Busca página i
         
         // Seta as propriedades da página.
         Element id = (Element) pageXML.getElementsByTagName( "id" ).item( 0 );
         Element name = (Element) pageXML.getElementsByTagName( "name" ).item( 0 );
         Element title = (Element) pageXML.getElementsByTagName( "title" ).item( 0 );

         p.setId( id.getTextContent() );
         p.setName( name.getTextContent() );
         p.setTitle( title.getTextContent() );

         // Gera o array com os conteúdos da página.
         Element contentsOfPage = (Element)pageXML.getElementsByTagName( "Content" ).item( 0 );

         // Busca os filhos da tag "Content" que são as imagens, textos, etc.
         NodeList contentsXML = contentsOfPage.getChildNodes();

         ArrayList<Content> contents = new ArrayList<Content>();
         for ( int j = 0; j < contentsXML.getLength(); j++ )
         {
        	 // Senão possio filhos, vai para o próximo ínidce do for.
        	 if( !contentsXML.item( j ).hasChildNodes() )
        		 continue;
        	 
        	 Element content = (Element) contentsXML.item( j );
        	 String nodeName = content.getNodeName();

            if ( nodeName.equals( "Text" ) )
            {
               SimpleText c = new SimpleText();
               id = (Element) content.getElementsByTagName( "id" ).item( 0 );
               Element text = (Element) content.getElementsByTagName( "text" ).item( 0 );
               Element font = (Element) content.getElementsByTagName( "font" ).item( 0 );
               Element size = (Element) content.getElementsByTagName( "size" ).item( 0 );
               
               c.setId( id.getTextContent() );
               
               c.setProperties( text.getTextContent(), font.getTextContent(), size.getTextContent() );
               contents.add( c );
            }
            else if ( nodeName.equals( "Image" ) )
            {
               SimpleImage c = new SimpleImage();
               id = (Element) content.getElementsByTagName( "id" ).item( 0 );
               Element extension = (Element) content.getElementsByTagName( "extension" ).item( 0 );
               Element path = (Element) content.getElementsByTagName( "path" ).item( 0 );
               Element width = (Element) content.getElementsByTagName( "width" ).item( 0 );
               Element height = (Element) content.getElementsByTagName( "height" ).item( 0 );
               Element legend = (Element) content.getElementsByTagName( "legend" ).item( 0 );
               
               c.setId( id.getTextContent() );
               c.setAllProperties( extension.getTextContent(),
                     path.getTextContent(), width.getTextContent(),
                     height.getTextContent(), legend.getTextContent() );
               contents.add( c );
            }
         }

         p.setContents( contents );
         pages.add( p );
      }

      object.setPages( pages );

   }
   
   /**
    *	Cria um objeto novo com todos os atributos zerados. 
 	*/
   public void clearObject()
   {
	   object = new LearningObject();
   }
   
   /**
    * Verifica qual o maior identificador de página existente.
    * 
    * @return maior identificar de página encontrado.
 	*/
   public int getMaxId()
   {
	   int maxId = 0;
	   
	   for( Page p : object.getPages() )
		   if( maxId < Integer.valueOf( p.getId() ) )
				   maxId = Integer.valueOf( p.getId() );
	   
	   return maxId;
   }
   
	/**
	 * Retorna o nome do objeto.
	 * 
	 * @return O nome do objeto.
	 */
	public String getObjectName()
	{
		return object.getName();
	}
	
	/**
	 * Atualiza o nome do objeto.
	 * 
	 * @param name Novo nome do objeto.
	 */
	public void setObjectName( String name )
	{
		object.setName( name );
	}
	
	/**
	 * Retorna as informações da imagem configurada como header do objeto.
	 * 
	 * @return Retorna o header do objeto.
	 */
	public SimpleImage getHeader()
	{
		return object.getHeader();
	}
	
	/**
	 * Retorna as informações da imagem configurada como footer do objeto.
	 * 
	 * @return Retorna o footer do objeto.
	 */
	public SimpleImage getFooter()
	{
		return object.getFooter();
	}

	/**
	 * Retorna o menu que o objeto corrente possui.
	 * 
	 * @return Objeto menu contendo seus links.
	 */
	public Menu getMenu()
	{
		return object.getMenu();
	}
	
	/**
	 * Retorna a página que possui o identificador passado.
	 * 
	 * @param id
	 *           Identificador da página que se deseja buscar.
	 * @return Página do objeto que possui o identificador passado.
	 */
	public Page getPage( String id )
	{
		return object.getPage( id );
	}
	
	/**
	 * Adiciona uma nova página ao objeto.
	 * 
	 * @param p Página a ser adicionada ao objeto.
	 */
	public void addPage( Page p )
	{
		object.getPages().add( p );
	}
	
	/**
	 * Exclui uma página do objeto.
	 * 
	 * @param p Página a ser excluída do objeto.
	 */
	public void removePage( Page p )
	{
		object.getPages().remove( p );
	}
}
